package org.ccay.core.exception;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;

import org.ccay.core.exception.impl.ExceptionMessageManager;
import org.ccay.core.ioc.Ccay;
import org.ccay.core.ioc.CcayApplicationContext;
import org.ccay.core.log.CcayLoggerFactory;
import org.ccay.core.log.ILogger;
import org.ccay.core.request.IRequestContext;
import org.ccay.core.request.IUserPrincipal;
import org.ccay.core.request.impl.RequestContext;
import org.ccay.core.request.impl.RequestContextConstants;
import org.ccay.core.util.HttpUtil;
import org.ccay.core.util.StreamUtil;
import org.ccay.core.util.StringUtil;

/**
 * 异常信息获取相关的类，此类严禁再往外抛出异常（会导致循环异常）
 * 
 * @since 2011-8-25
 */
public final class ExceptionHandler {
	/**
	 * 异常最大挖掘层数
	 */
	private static final int MAX_MIND_LEVEL = 10;


	/**
	 * 避免创建实例
	 */
	private ExceptionHandler(){		
	}
	public static final String MISSED_MESSAGE = "Missed Message[See log for detail]:";
	private static final ILogger logger = CcayLoggerFactory.getLogger(ExceptionHandler.class);
	
	/**
	 * 获取本地化的异常信息
	 * @param ex 异常
	 * @return 本地化的异常信息
	 */
	public static String getExceptionMessage(String errorCode){
		 return ExceptionMessageManager.getMessage(errorCode);
	}

	/**
	 * @return
	 */
	public static String getExceptionMessage(String errorCode,Object... args) {
		 return ExceptionMessageManager.getMessage(errorCode, args);
	}
	
	/** 
	 * 获取友好错误
	 *  
	 * @since 2012-2-13
	 * @param ex 异常
	 * @return 友好错误
	 */
	public static FaultVO getFriendlyFault(Throwable ex){		
		return getFriendlyFault(ex,true);
	}
	
	/** 
	 * 获取友好错误
	 * 
	 * @since 2013-3-28
	 * @param ex 异常
	 * @param convert 是否尝试转换异常
	 * @return
	 */
	public static FaultVO getFriendlyFault(Throwable ex, boolean convert){		
		FaultVO fault = new FaultVO();
		//判断是Ccay异常体系的异常
		if(convert && !(ex instanceof ICodeProvided)){
			ex = convert(ex);
		}
        
        handlerMessage(ex, fault);
        handlerCode(ex, fault);
        fault.setFaultUid(Thread.currentThread().getName());
        
        return fault;
	}

	/** 
	 * 从异常链中挖掘出Ccay异常
	 * @param ex
	 * @return
	 */
	public static Throwable digException(Throwable ex) {
		Throwable temp = ex;
		for(int i = 0 ; i < MAX_MIND_LEVEL ; i++){
			if(temp instanceof ApplicationException || temp instanceof SystemException){
				if(i > 0){
					logger.debug("After friendly exception found at level {0} for {1}/{2}",i, ex.getClass(),ex.getMessage());
				}
				return temp;
			}
			
			if(temp.getCause() == null){
				//未找到，返回原始异常
				return convert(ex);
			}
			temp = temp.getCause();
		}
		//未找到，返回原始异常
		return convert(ex);
	}

	/** 
	 * 处理异常码
	 * 
	 * @since 2012-2-14
	 * @param ex
	 * @param fault
	 */
	private static void handlerCode(Throwable ex, FaultVO fault) {
		if(ex instanceof ICodeProvided){
        	fault.setCode( ((ICodeProvided)ex).getErrorCode() );
        }else{
        	fault.setCode( "unknown");
        }
		
		if(ex instanceof IHttpCodeProvided){
			fault.setHttpCode( ((IHttpCodeProvided)ex).getHttpCode());
		}
		
		if(ex instanceof IEntityRelated){
			fault.setEntity(((IEntityRelated)ex).getEntity());
		}
	}

	/** 
	 * 处理异常Message	
	 * 
	 * @since 2012-2-14
	 * @param ex
	 * @param fault
	 */
	private static void handlerMessage(Throwable ex, FaultVO fault) {
		if(ex instanceof IFriendlyMessage){
        	//提供友好Message
        	fault.setMessage( ex.getMessage());
        } else {
        	// begin modify by zwx304259 sp20  获取堆栈信息
			Writer sw = new StringWriter();
			PrintWriter printWriter = new PrintWriter(sw);
			ex.printStackTrace(printWriter);

			fault.setStackTrace(sw.toString());

			StreamUtil.closeStreams(printWriter, sw);

			fault.setMessage(ExceptionMessageManager.getSystemErrorMessage());

			// end modify by zwx304259 sp20  获取堆栈信息
        }        
	}
	
	/** 
	 * 转换异常	
	 * 
	 * @since 2012-2-14
	 * @param ex 原始异常
	 * @return 转换后的异常
	 */
	public static Throwable convert(Throwable ex){
		String exName = ex.getClass().getName();
		//转换器的命名规则
		String beanName = "IExceptionConverter." + exName;
		CcayApplicationContext ccay = Ccay.getContext();
		
		if(!ccay.containsBean(beanName)){
			logger.debug("No converter found.Ccay could not convert exception:" + exName);
			return ex;
		}
		logger.debug("Before convert exception:" + exName);
		
		IExceptionConverter iec = ccay.getBean(beanName,IExceptionConverter.class);		
		return iec.convert(ex);
	}
	
	/** 
	 * 将异常记录到日志里，并同时记录当前用户和当前正在执行的URI
	 * @param e 异常
	 */
	public  static void logException(Throwable e){
		if(!(e instanceof IHttpCodeProvided) || ((IHttpCodeProvided)e).getHttpCode() >= HttpUtil.SERVER_ERROR ){
			logger.error(getMessage2Log(e), e);
		}else{
			logger.warn(getMessage2Log(e));
		}
	}
	
	/** 
	 * 获取记录到日志中的异常信息
	 * @param request
	 * @return
	 */
	private static String getMessage2Log(Throwable ex) {
		IRequestContext requestContext = RequestContext.getCurrent(true);		
		String uri = null;
		String userAccount = null;	
		
		if( requestContext != null ){
			uri = (String)requestContext.getItem(RequestContextConstants.KEY_URI);
			
			IUserPrincipal user = requestContext.getUser();
			
			if( user != null){
				userAccount = user.getUserName();
			}			
		}
		String code  = null;
		if(ex instanceof ICodeProvided){
			code = ((ICodeProvided)ex).getErrorCode();
		}
		return StringUtil.join(new String[] { "Code:[", code, "],User:[", userAccount, "],[Error:",ex.getMessage(),"], URI:[", uri,
				"]" }, StringUtil.EMPTY);
	}

}
